package script;

import java.util.ArrayList;

import main.ErrorException;
import define.Common;
import define.Config;
import define.ErrorDefine;
import define.ExpressType;
import define.FunctionType;
import define.Keyword;

public class Express extends VariableContainer
{
	private Script m_script;
	
	private int m_start;
	private int m_end;
	private ExpressType m_type;
	private ArrayList<Express> m_children;
	private String m_value;
	private BaseExpress m_valueExpress;
	
	public Express(Script script, int start, int end, VariableContainer father) throws ErrorException
	{
		super(father, script.getGlobalVariables());
		m_script = script;
		m_start = start;
		m_end = end;
		m_value = "";
		m_valueExpress = null;
		makeType();
	}
	
	private void makeType() throws ErrorException
	{
		Keyword key = Keyword.isRelative(m_script.m_scriptCode[m_start]);
		if(key != null && Common.findRelative(m_start, m_script.m_scriptCode, m_end) == m_end)
		{
			makeRelativeChildren();
			return;
		}
		int index = findKeyword(m_start);
		if(index != Config.NO_FIND)
		{
			m_type = ExpressType.E_MIX_EXP_TYPE;
			m_children = new ArrayList<Express>();
			makeMixChildren();
		}
		else
		{
			m_type = ExpressType.E_STRING_EXP_TYPE;
			m_value = Common.buildString(m_script.m_scriptCode, m_start, m_end);
		}
	}
	
	private void makeRelativeChildren() throws ErrorException
	{
		if(m_script.m_scriptCode[m_start] == Keyword.E_CELL_KEY.getCode())
		{
			m_type = ExpressType.E_CELL_EXP_TYPE;
			m_valueExpress = new Cell(m_script, m_start, m_end, m_father);
		}
		else if(m_script.m_scriptCode[m_start] == Keyword.E_VARIABLE_KEY.getCode())
		{
			m_type = ExpressType.E_VARIABLE_EXP_TYPE;
			m_valueExpress = new Variable(m_script, m_start, m_end);
		}
		else if(m_script.m_scriptCode[m_start] == Keyword.E_FUN_START_KEY.getCode())
		{
			m_type = ExpressType.E_FUNCTION_EXP_TYPE;
			m_valueExpress = new Function(m_script, m_start, m_end, m_father);
		}
		else
		{
			m_type = ExpressType.E_NONE_EXP_TYPE;
		}
	}
	
	private void makeMixChildren() throws ErrorException
	{
		int start = m_start;
		Keyword key = null;
		int end = m_start;
		while (start <= m_end) 
		{
			key = Keyword.isRelative(m_script.m_scriptCode[start]);
			if(key != null)
			{
				end = Common.findRelative(start, m_script.m_scriptCode, m_end);
				if(end == Config.NO_FIND)
				{
					throw new ErrorException(ErrorDefine.E_EXP_NOT_MATCH_ERROR, start);
				}
				m_children.add(new Express(m_script, start, end, this));
				start = end + 1;
			}
			else 
			{
				end = findKeyword(start);
				if(end == Config.NO_FIND)
				{
					end = m_end;
					m_children.add(new Express(m_script, start, end, this));
					start = end + 1;
				}
				else if(end == start)
				{
					throw new ErrorException(ErrorDefine.E_EXP_NOT_MATCH_ERROR, start);
				}
				else
				{
					m_children.add(new Express(m_script, start, end - 1, this));
					start = end;
				}
			}
		}
	}
	
	private int findKeyword(int startPosition)
	{
		for(; startPosition <= m_end; ++startPosition)
		{
			Keyword key = Keyword.isExpressCheck(m_script.m_scriptCode[startPosition]);
			if(key != null)
			{
				return startPosition;
			}
		}
		return Config.NO_FIND;
	}
	
	public StringBuffer getValue() throws ErrorException
	{
		if(Common.m_isBreakFor)
		{
			return new StringBuffer("");
		}
		switch(m_type) 
		{
		case E_VARIABLE_EXP_TYPE:
			return getVariableValue(m_valueExpress);
		case E_CELL_EXP_TYPE:
			return m_valueExpress.getValue();
		case E_STRING_EXP_TYPE:
			return new StringBuffer(m_value);
		case E_FUNCTION_EXP_TYPE:
			return ((Function)m_valueExpress).getValue();
		case E_MIX_EXP_TYPE:
		{
			StringBuffer temp = new StringBuffer();
			for(int i = 0, max = m_children.size(); i < max; ++i)
			{
				temp.append(m_children.get(i).getValue());
			}
			return temp;
		}
		case E_NONE_EXP_TYPE:
		default:
			return null;
		}
	}
	
	public int getStart()
	{
		return m_start;
	}
	
	public int getEnd()
	{
		return m_end;
	}

	public FunctionType getFunctionType()
	{
		return null;
	}
}
